﻿/*
   Copyright 2012 Dmitry Bratus

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Flower.Services.Data;
using Jurassic.Library;
using System.Collections.ObjectModel;

namespace Flower.Directory.Scripting
{
    internal static class ObjectInstanceMappingExtension
    {
        #region JS type name

        private static string GetJsTypeName(object obj)
        {
            if (obj == null)
            {
                return "undefined";
            }
            else if (obj is Jurassic.Null)
            {
                return "null";
            }
            else if (obj is StringInstance)
            {
                return "String";
            }
            else if (obj is NumberInstance)
            {
                return "Number";
            }
            else if (obj is BooleanInstance)
            {
                return "Boolean";
            }
            else if (obj is ArrayInstance)
            {
                return "Array";
            }
            else if (obj is DateInstance)
            {
                return "Date";
            }
            else if (obj is RegExpInstance)
            {
                return "RegEx";
            }
            else if (obj is FunctionInstance)
            {
                return "Function";
            }
            else if (obj is ObjectInstance)
            {
                return "Object";
            }
            else
            {
                return obj.GetType().FullName;
            }
        }

        #endregion

        public static ObjectInstance Set(this ObjectInstance obj, string propertyName, ObjectInstance value)
        {
            obj[propertyName] = value;
            return obj;
        }

        public static ObjectInstance Set(this ObjectInstance obj, string propertyName, string value)
        {
            obj[propertyName] = (object)value ?? Jurassic.Null.Value;
            return obj;
        }

        public static ObjectInstance Set(this ObjectInstance obj, string propertyName, ICollection<string> value)
        {
            var arr = obj.Engine.Array.Construct();

            foreach (string s in value)
            {
                arr.Push(s);
            }

            obj[propertyName] = arr;
            return obj;
        }

        public static ObjectInstance Set(this ObjectInstance obj, string propertyName, ICollection<bool> value)
        {
            var arr = obj.Engine.Array.Construct();

            foreach (bool s in value)
            {
                arr.Push(s);
            }

            obj[propertyName] = arr;
            return obj;
        }

        public static ObjectInstance Set(this ObjectInstance obj, string propertyName, ICollection<int> value)
        {
            var arr = obj.Engine.Array.Construct();

            foreach (int s in value)
            {
                arr.Push(s);
            }

            obj[propertyName] = arr;
            return obj;
        }

        public static ObjectInstance Set(this ObjectInstance obj, string propertyName, ICollection<double> value)
        {
            var arr = obj.Engine.Array.Construct();

            foreach (double s in value)
            {
                arr.Push(s);
            }

            obj[propertyName] = arr;
            return obj;
        }

        public static ObjectInstance Set(this ObjectInstance obj, string propertyName, int value)
        {
            obj[propertyName] = value;
            return obj;
        }

        public static ObjectInstance Set(this ObjectInstance obj, string propertyName, bool value)
        {
            obj[propertyName] = value;
            return obj;
        }

        public static ObjectInstance Set(this ObjectInstance obj, string propertyName, double value)
        {
            obj[propertyName] = value;
            return obj;
        }

        public static ObjectInstance Set(this ObjectInstance obj, string propertyName, DateTime value)
        {
            obj[propertyName] = 
                obj.Engine.Date.Construct(value.Year, value.Month - 1, value.Day, value.Hour, value.Minute, value.Second, value.Millisecond);
            return obj;
        }

        public static ObjectInstance Set(this ObjectInstance obj, string propertyName, ICollection<AccessControlEntry> value)
        {
            var arr = obj.Engine.Array.Construct();

            foreach (AccessControlEntry perm in value)
            {
                var permObj = obj.Engine.Object.Construct();

                permObj.Set("role", perm.Role);
                permObj.Set("permission", (int)perm.Permission);

                arr.Push((object)permObj);
            }

            obj[propertyName] = arr;
            return obj;
        }

        public static ObjectInstance Set(this ObjectInstance obj, string propertyName, Blob value)
        {
            obj[propertyName] = (value != null) ? (object)new XmlObject(obj.Engine.Object.InstancePrototype, value) : Jurassic.Null.Value;
            return obj;
        }

        public static string GetString(this ObjectInstance obj, string propertyName)
        {
            var val = obj.GetPropertyValue(propertyName);
            if (val == null)
            {
                return null;
            }

            if (val is Jurassic.Null)
            {
                return null;
            }

            var valString = val as string;
            if (valString != null)
            {
                return valString;
            }

            var str = val as StringInstance;

            if (str != null)
            {
                return str.Value;
            }
            else
            {
                var strConcat = val as Jurassic.ConcatenatedString;

                if (strConcat != null)
                {
                    return strConcat.ToString();
                }
                else
                {
                    throw new PropertyMappingException(propertyName, typeof(string), GetJsTypeName(val));
                }
            }
        }

        public static ICollection<string> GetStrings(this ObjectInstance obj, string propertyName)
        {
            var val = obj.GetPropertyValue(propertyName);
            if (val == null)
            {
                return null;
            }

            if (val is Jurassic.Null)
            {
                return null;
            }

            var arr = val as ArrayInstance;

            if (arr != null)
            {
                var coll = new List<string>();

                foreach (object el in arr.ElementValues)
                {
                    if (el is Jurassic.Null)
                    {
                        coll.Add(null);
                        continue;
                    }

                    var elString = el as string;
                    if (elString != null)
                    {
                        coll.Add(elString);
                        continue;
                    }

                    var str = el as StringInstance;
                    if (str != null)
                    {
                        coll.Add(str.Value);
                    }
                    else
                    {
                        throw new PropertyMappingException(propertyName, typeof(string), GetJsTypeName(el));
                    }
                }

                return coll;
            }
            else
            {
                throw new PropertyMappingException(propertyName, typeof(List<string>), GetJsTypeName(val));
            }
        }

        public static int GetInt(this ObjectInstance obj, string propertyName)
        {
            var val = obj.GetPropertyValue(propertyName);
            if (val == null)
            {
                return default(int);
            }

            if (val is Jurassic.Null)
            {
                throw new PropertyMappingException(propertyName, typeof(int), GetJsTypeName(val));
            }

            if (val is int)
            {
                return (int)val;
            }

            var num = val as NumberInstance;

            if (num != null)
            {
                return (int)num.Value;
            }
            else
            {
                throw new PropertyMappingException(propertyName, typeof(int), GetJsTypeName(val));
            }
        }

        public static DateTime GetDateTime(this ObjectInstance obj, string propertyName)
        {
            var val = obj.GetPropertyValue(propertyName);
            if (val == null)
            {
                return default(DateTime);
            }

            if (val is Jurassic.Null)
            {
                throw new PropertyMappingException(propertyName, typeof(DateTime), GetJsTypeName(val));
            }

            var date = val as DateInstance;

            if (date != null)
            {
                return date.Value;
            }
            else
            {
                throw new PropertyMappingException(propertyName, typeof(DateTime), GetJsTypeName(val));
            }
        }

        public static ICollection<AccessControlEntry> GetAccessControlList(this ObjectInstance obj, string propertyName)
        {
            var val = obj.GetPropertyValue(propertyName);
            if (val == null)
            {
                return new List<AccessControlEntry>();
            }

            if (val is Jurassic.Null)
            {
                return new List<AccessControlEntry>();
            }

            var arr = val as ArrayInstance;

            if (arr != null)
            {
                var coll = new List<AccessControlEntry>();

                foreach (object item in arr.ElementValues)
                {
                    var permObj = item as ObjectInstance;
                    if (permObj == null)
                    {
                        throw new PropertyMappingException(propertyName, typeof(AccessControlEntry), GetJsTypeName(item));
                    }

                    coll.Add
                    (
                        new AccessControlEntry
                        {
                            Role = permObj.GetString("role"),
                            Permission = (Permission)permObj.GetInt("permission")
                        }
                    );
                }

                return coll;
            }
            else
            {
                throw new PropertyMappingException(propertyName, typeof(List<AccessControlEntry>), GetJsTypeName(val));
            }
        }

        public static Blob GetBlob(this ObjectInstance obj, string propertyName)
        {
            var val = obj.GetPropertyValue(propertyName);
            if (val == null)
            {
                return null;
            }

            if (val is Jurassic.Null)
            {
                return null;
            }

            var xml = val as XmlObject;

            if (xml != null)
            {
                return xml.ToBlob();
            }
            else
            {
                throw new PropertyMappingException(propertyName, typeof(Blob), GetJsTypeName(val));
            }
        }
    }
}
